/**
 * Created by chencheng on 2017/6/16.
 */
import moment from 'moment';
import _ from 'lodash';

import deepClone from './deepClone';

class Helper {
    deepClone = deepClone;

    /**
     * @param {*} x
     * @return {{index: *, base: string}}
     */
    getNumberBase(x) {
      const baseCollection = ['B', 'K', 'M', 'G', 'T'];
      x = _.toFinite(Math.abs(x));
      if (x < 1024) {
        return {
          index: 0,
          base: baseCollection[0]
        };
      } else {
        let index = Math.floor(Math.log(x) / Math.log(1024));
        index = index >= baseCollection.length ? baseCollection.length - 1 : index;
        return {
          index,
          base: baseCollection[index]
        };
      }
    }

    formatSmallNumber(x) {
      return x < 10 ? '0' + x : String(x);
    }

    /**
     * 防抖函数
     * @param {Function} fn     回调函数
     * @param {Number} delay    延迟事件
     * @param {Object} [context]  回调函数上下文
     * @returns {Function}
     */
    debounce = (fn, delay, context) => {
      let timeout;

      return function(e) {
        clearTimeout(timeout);

        context = context || this;
        const args = arguments;

        timeout = setTimeout(function() {
          fn.apply(context, args);
        }, delay);
      };
    };

    /**
     * 文件下载
     * @param url 下载地址（非请求）
     */
    downloadFile(url) {
      const domId = 'tj-iframe-downloadFile';
      const iframeDom = $('<iframe />').css({
        display: 'none'
      }).attr('src', url);
      if ($('#' + domId).length < 1) {
        $('<div />', {
          id: domId
        }).html(iframeDom).appendTo('body');
      }

      const domObject = document.querySelector('#' + domId);
      $(domObject).html(iframeDom);
    }

    /**
     * 模拟request api数据
     * @param data
     * @param type "success" | "error"
     * @param time 延迟时间
     * @return {Promise<any>}
     */
    simulateData(data, type = 'success', time = 500) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (type == 'success') {
            resolve({ code: window.ENV.apiSuccessCode, data, msg: 'success' });
          } else {
            reject({ code: 'error', data, msg: 'error' });
          }
        }, time);
      });
    }

    /**
     * 跳转页面
     * @param {String} url
     * @param {Mixed} timeout
     */
    redirect(url, timeout = 0) {
      if (_.isNumber(url) && typeof timeout === 'undefined') {
        timeout = url;
        url = null;
      }

      setTimeout(function() {
        location.href = url || location.href;
      }, timeout || 0);
    }

    /**
     * 获取包“immutability-helper”中的update内容
     * @param {String | Array}  keyPath "str1.str2..." //依点分割key 或 [key1,key2]
     * @param {Mixed} value
     * @param {String} updateType //可填写类型：$set,$push,$unshift,$splice,$unset,$merge,$apply
     * @return {{}}
     */
    getImmutabilityHelperContent(keyPath, value, updateType = '$set') {
      const keyArr = Array.isArray(keyPath) ? keyPath : keyPath.split('.');
      const keyLen = keyArr.length;
      const result = {};

      /* eslint no-eval:0 */
      /* eslint no-return-assign:0 */
      const getTmpRes = (keys, val = null) => {
        let res = 'result';
        keys.forEach(key => res += '[\'' + key + '\']');
        res += '={}';
        eval(res);
        return eval(res.replace('={}', ''));
      };

      const usedKeys = [];
      keyArr.forEach((key, index) => {
        const currentLen = index + 1;
        usedKeys.push(key);

        if (currentLen === keyLen) {
          getTmpRes(usedKeys)[updateType] = value;
        } else {
          getTmpRes(usedKeys);
        }
      });

      return result;
    }

    /**
     * 将对象值中的唯一值字段作为key组成mapping
     * @param enumInfo
     * @param uniqueField
     * @returns {{}}
     *
     * usage:
     * enumInfo = {
     *     xx: {
     *          type: 1,
     *          ...
     *     }
     * }
     */
    unqiueTypeToInfoMap(enumInfo = {}, uniqueField = 'value') {
      const typeToInfoMap = {};
      Object.values(enumInfo).forEach(item => typeToInfoMap[item[uniqueField]] = item);
      return typeToInfoMap;
    }

    /**
     * 当值为undefined || null时返回'-',否则返回本身
     * @param {Mixed} defaultValue
     * @return {function(*=)}
     */
    identityVal(defaultValue = '-') {
      return (value) => {
        try {
          return _.isUndefined(value) || _.isNull(value) ? defaultValue : value;
        } catch (e) {
          return defaultValue;
        }
      };
    }

    /**
     * 浮点型保留小数
     * @param {Number} num
     * @param {Number} fixNum
     * @param {Mixed} defaultVal 格式化错误的默认值
     * @return {string}
     */
    toFixed(num, fixNum = 2, defaultVal = '-') {
      if (!this.isFloat(num)) return num;
      const result = Number(num).toFixed(fixNum);
      return _.isNaN(result) || result === 'NaN' ? defaultVal : result;
    }

    /**
     * 验证是否为浮点型
     * @param {Mixed} theFloat
     * @return {boolean}
     */
    isFloat(theFloat) {
      if (_.isNumber(theFloat)) theFloat = theFloat.toString();
      const len = theFloat.length;
      let dotNum = 0;

      if (len === 0) return false;

      for (let i = 0; i < len; i++) {
        const oneNum = theFloat.substring(i, i + 1);
        if (oneNum === '.') dotNum++;
        if ((oneNum < '0' || oneNum > '9') && oneNum !== '.') return false;
      }

      if (len > 1 && theFloat.substring(0, 1) === '0') {
        if (theFloat.substring(1, 2) !== '.') return false;
      }

      if (dotNum === 0 || dotNum > 1) return false;

      return true;
    }

    /**
     * 是否是一个真实的数值字符串
     * @param value
     */
    isRealNumeric(value) {
      return /^(\d+\.)?\d+$/.test(value);
    }

    /**
     * Mb转换成Gb
     * @param {Number} value
     * @param {Number} fixNum    保留小数的位数
     * @param {Mixed} defaultVal    格式化错误的默认值
     * @return {*}
     * @constructor
     */
    MbToGb(value, fixNum = 2, defaultVal = '-') {
      const result = value / 1024;
      return _.isNaN(result) || result === 'NaN' ? defaultVal : this.toFixed(result, fixNum, defaultVal);
    }

    /**
     * Kb转换成Mb
     * @param {Number} value
     * @param {Number} fixNum    保留小数的位数
     * @param {Mixed} defaultVal    格式化错误的默认值
     * @return {*}
     * @constructor
     */
    KbToGb(value, fixNum = 2, defaultVal = '-') {
      const result = value / 1024;
      return _.isNaN(result) || result === 'NaN' ? defaultVal : this.toFixed(result, fixNum, defaultVal);
    }

    /**
     * byte转换成 kb
     * @param {Number} value
     * @param {Number} fixNum    保留小数的位数
     * @param {Mixed} defaultVal    格式化错误的默认值
     * @return {*}
     * @constructor
     */
    byteToKb(value, fixNum = 2, defaultVal = '-') {
      const result = value / 1024;
      return _.isNaN(result) || result === 'NaN' ? defaultVal : this.toFixed(result, fixNum, defaultVal);
    }

    /**
     * 时间格式化
     * @param {Date|number|Moment} date
     * @param {string} template
     * @return {string}
     */
    dateFormat(date = _.now(), template = 'YYYY-MM-DD HH:mm:ss') {
      try {
        if (this.isRealNumeric(date)) {
          date = parseInt(date);
        }

        return moment(date).format(template);
      } catch (e) {
        return '-';
      }
    }

    moment = () => moment;

    /**
     * 回调一个函数,并应用context和一个参数数组
     * @param {function} func 函数
     * @param {?*=} context
     * @param {Array=} args
     * @return {*}
     */
    apply(func, context, args = []) {
      if (_.isFunction(func)) {
        return func.apply(context, args);
      }

      return null;
    }

    /**
     * 回调一个函数,并应用context和一个参数列表
     * @param {function} func 函数
     * @param {?*=} context
     * @param {...*} args
     * @return {*}
     */
    call(func, context, ...args) {
      return this.apply(func, context, args);
    }

    /**
     * 获取queryString
     *
     * @returns {Object}
     */
    getUrlParams() {
      const urlParams = {};
      const queryString = decodeURIComponent(window.location.search.substr(1));
      if (queryString) {
        _.each(queryString.split('&'), (paramString) => {
          const param = paramString.split('=');
          urlParams[param[0]] = param[1];
        });
      }

      return urlParams;
    }

    /**
     * 获取URL参数
     *
     * @param {string} name
     * @returns {string}
     */
    getUrlParam(name) {
      return this.getUrlParams()[name];
    }

    /**
     * 省去对象的字段
     * @param {Object} obj
     * @param {Array} fields 排除的字段
     * @return {{}}
     */
    omit(obj, fields = []) {
      const shallowCopy = {
        ...obj
      };
      for (let i = 0; i < fields.length; i++) {
        const key = fields[i];
        delete shallowCopy[key];
      }
      return shallowCopy;
    }

    /**
     * 数字格式化
     * @param {number} number
     * @param {number=} decimals
     * @param {string=} decimalPoint
     * @param {string=} thousandsSep
     * @return {string}
     */
    numberFormat(number, decimals = 0, decimalPoint = '.', thousandsSep = ',') {
      number = _.toNumber(number);
      decimals = _.clamp(decimals, 0, 10);

      const origDec = (number.toString().split('.')[1] || '').length;
      const absNumber = Math.abs(number);

      let decimalComponent;
      let ret;

      if (decimals === -1) {
        decimals = Math.min(origDec, 20);
      } else if (!_.isNumber(decimals)) {
        decimals = 2;
      }

      const strinteger = String(parseInt(absNumber.toFixed(decimals), 10));

      // 需要几个千分位分隔符
      const thousands = strinteger.length > 3 ? strinteger.length % 3 : 0;

      // 负数
      ret = number < 0 ? '-' : '';

      ret += thousands ? strinteger.substr(0, thousands) + thousandsSep : '';

      // 在千分位加上分隔符
      ret += strinteger.substr(thousands).replace(/(\d{3})(?=\d)/g, `$1${thousandsSep}`);

      // 小数点
      if (decimals) {
        decimalComponent = Math.abs((absNumber - strinteger) + (Math.pow(10, -Math.max(decimals, origDec) - 1)));

        ret += decimalPoint + decimalComponent.toFixed(decimals).slice(2);
      }

      return ret;
    }

    /**
     * 生成uuid
     * @param {Number} len
     * @returns {string}
     */
    uuid(len = 16) {
      const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
      const uuid = [];
      let i;
      const radix = chars.length;

      if (len) {
        // Compact form
        for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
      } else {
        // rfc4122, version 4 form
        let r;

        // rfc4122 requires these characters
        uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
        uuid[14] = '4';

        // Fill in random data. At i==19 set the high bits of clock sequence as
        // per rfc4122, sec. 4.1.5
        for (i = 0; i < 36; i++) {
          if (!uuid[i]) {
            r = 0 | Math.random() * 16;
            uuid[i] = chars[(i === 19) ? (r & 0x3) | 0x8 : r];
          }
        }
      }

      return uuid.join('');
    }

    /**
     * antd组件的排序函数
     * @param nowObject  当前的对象
     * @param nextObject 下一个对象
     * @param sortProperty 排序的字段
     * @returns {number}
     */
    sortFunction(nowObject, nextObject, sortProperty) {
      return nowObject[sortProperty] > nextObject[sortProperty]
        ? 1
        : nowObject[sortProperty] === nextObject[sortProperty]
          ? 0
          : -1;
    }

    /**
     * @param x 字节大小
     * @param w 保留小数位数
     * @returns {string}
     */
    autoToSize(x, w = 2) {
      const base = this.getNumberBase(x);
      return _.round(Math.abs(_.toFinite(x)) / Math.pow(1024, base.index), w) + base.base;
    }

    /**
     * 数组去重
     * @param {Array} data
     * @return {*[]}
     */
    uniq = (data) => Array.from(new Set(data));

    /**
    * Created by PanJiaChen on 16/11/18.
    */

    /**
   * Parse the time to string
   * @param {(Object|string|number)} time
   * @param {string} cFormat
   * @returns {string | null}
   */
    parseTime(time, cFormat) {
      if (arguments.length === 0 || !time) {
        return null;
      }
      const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}';
      let date;
      if (typeof time === 'object') {
        date = time;
      } else {
        if ((typeof time === 'string')) {
          if ((/^[0-9]+$/.test(time))) {
          // support "1548221490638"
            time = parseInt(time);
          } else {
          // support safari
          // https://stackoverflow.com/questions/4310953/invalid-date-in-safari
            time = time.replace(new RegExp(/-/gm), '/');
          }
        }

        if ((typeof time === 'number') && (time.toString().length === 10)) {
          time = time * 1000;
        }
        date = new Date(time);
      }
      const formatObj = {
        y: date.getFullYear(),
        m: date.getMonth() + 1,
        d: date.getDate(),
        h: date.getHours(),
        i: date.getMinutes(),
        s: date.getSeconds(),
        a: date.getDay()
      };
      const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
        const value = formatObj[key];
        // Note: getDay() returns 0 on Sunday
        if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ]; }
        return value.toString().padStart(2, '0');
      });
      return time_str;
    }

    /**
   * @param {number} time
   * @param {string} option
   * @returns {string}
   */
    formatTime(time, option) {
      if (('' + time).length === 10) {
        time = parseInt(time) * 1000;
      } else {
        time = +time;
      }
      const d = new Date(time);
      const now = Date.now();

      const diff = (now - d) / 1000;

      if (diff < 30) {
        return '刚刚';
      } else if (diff < 3600) {
      // less 1 hour
        return Math.ceil(diff / 60) + '分钟前';
      } else if (diff < 3600 * 24) {
        return Math.ceil(diff / 3600) + '小时前';
      } else if (diff < 3600 * 24 * 2) {
        return '1天前';
      }
      if (option) {
        return this.parseTime(time, option);
      } else {
        return (
          d.getMonth() +
        1 +
        '月' +
        d.getDate() +
        '日' +
        d.getHours() +
        '时' +
        d.getMinutes() +
        '分'
        );
      }
    }

    /**
   * @param {string} url
   * @returns {Object}
   */
    param2Obj(url) {
      const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ');
      if (!search) {
        return {};
      }
      const obj = {};
      const searchArr = search.split('&');
      searchArr.forEach(v => {
        const index = v.indexOf('=');
        if (index !== -1) {
          const name = v.substring(0, index);
          const val = v.substring(index + 1, v.length);
          obj[name] = val;
        }
      });
      return obj;
    }

    /**
   * 数据分页
   * @param page
   * @param pageSize
   * @param totalData
   * @returns {{data: Array, page: number, pageSize: number, total: Array.length}}
   */
    paginationData(page = 1, pageSize = 10, totalData = []) {
      const { length } = totalData;
      const tableData = {
        data: [],
        page,
        pageSize,
        total: length
      };
      if (pageSize >= length) { // pageSize大于等于总数据长度，说明只有1页数据或没有数据
        tableData.data = totalData;
        tableData.page = 1;// 直接取第一页
      } else { // pageSize小于总数据长度，数据多余1页
        const num = pageSize * (page - 1);// 计算当前页（不含）之前的所有数据总条数
        if (num < length) { // 如果当前页之前所有数据总条数小于（不能等于）总的数据集长度，则说明当前页码没有超出最大页码
          const startIndex = num;// 当前页第一条数据在总数据集中的索引
          const endIndex = num + pageSize - 1;// 当前页最后一条数据索引
          tableData.data = totalData.filter((_, index) => index >= startIndex && index <= endIndex);// 当前页数据条数小于每页最大条数时，也按最大条数范围筛取数据
        } else { // 当前页码超出最大页码，则计算实际最后一页的page，自动返回最后一页数据
          const size = parseInt(length / pageSize); // 取商
          const rest = length % pageSize; // 取余数
          if (rest > 0) { // 余数大于0，说明实际最后一页数据不足pageSize，应该取size+1为最后一条的页码
            tableData.page = size + 1;// 当前页码重置，取size+1
            tableData.data = totalData.filter((_, index) => index >= (pageSize * size) && index <= length);
          } else if (rest === 0) { // 余数等于0，最后一页数据条数正好是pageSize
            tableData.page = size;// 当前页码重置，取size
            tableData.data = totalData.filter((_, index) => index >= (pageSize * (size - 1)) && index <= length);
          } // 注：余数不可能小于0
        }
      }
      return tableData;
    }

    /**
   * 转换Key
   * @param obj
   * @param keyMap
   * @param isDeep
   * @returns {*}
   */
    convertKey(obj = {}, keyMap = {}, isDeep = true) {
      if (!['[object Array]', '[object Object]'].includes(Object.prototype.toString.call(obj))) {
        throw new TypeError('The first argument should be either an object or an array！');
      }
      if (Object.prototype.toString.call(keyMap) !== '[object Object]') {
        throw new TypeError('The parameter keyMap should be an object!');
      }
      const res = obj instanceof Array ? [] : {};
      if (obj instanceof Object) {
        for (const key in obj) {
          const newKey = Object.keys(keyMap).includes(key) ? keyMap[key] : key;
          res[newKey] = obj[key];

          // 是否为深度转换
          if (isDeep && obj[key] instanceof Object && Object.keys(obj[key]).length) {
            res[newKey] = this.convertKey(obj[key], keyMap, isDeep);
          }
        }
      }
      return res;
    }
}

const helper = new Helper();

export default helper;

